﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Windows.Forms;

using TraitorAmongUs.Exceptions;

namespace TraitorAmongUs.Network {
	public class Client {
		TcpClient tcpClient;
		Thread clientThread;

		public TcpClient TCPClient {
			get {
				return tcpClient;
			}
		}

		public NetworkStream Stream {
			get {
				return tcpClient.GetStream();
			}
		}

		// Packet events
		#region Packet Events
		// Error Packets
		public delegate void ServerFullPacketReceivedHandler();
		public event ServerFullPacketReceivedHandler OnServerFullPacketReceived;

		// Lobby
		public delegate void VersionPacketReceivedHandler();
		public event VersionPacketReceivedHandler OnVersionPacketReceived;

		public delegate void ChatPacketReceievedHandler(string username, string message);
		public event ChatPacketReceievedHandler OnChatPacketReceived;

		public delegate void StartGameReceivedHandler();
		public event StartGameReceivedHandler OnStartGamePacketReceived;

		// Game
		public delegate void InitialDataReceivedHandler(ServerPlayer[] playerList);
		public event InitialDataReceivedHandler OnInitialDataPacketReceived;

		public delegate void UpdateDataReceivedHandler(ServerPlayer[] playerList);
		public event UpdateDataReceivedHandler OnUpdateDataPacketReceived;

		public delegate void UpdateCrosshairReceivedHandler(Crosshair[] crosshairList);
		public event UpdateCrosshairReceivedHandler OnUpdateCrosshairPacketReceived;

		public delegate void EveryoneLoadedReceivedHandler();
		public event EveryoneLoadedReceivedHandler OnEveryoneLoadedPacketReceived;

		public delegate void UpdateBulletCountReceivedHandler(int bulletCount);
		public event UpdateBulletCountReceivedHandler OnUpdateBulletCountPacketReceived;

		public delegate void WinnerReceivedHandler(string username);
		public event WinnerReceivedHandler OnWinnerPacketReceived;
		#endregion

		public Client() {
		}

		public void ConnectToServer(string hostname, int port) {
			try {
				tcpClient = new TcpClient();
				tcpClient.Connect(hostname, port);
			} catch(Exception e) {
				MessageBox.Show(string.Format("Failed to connect to the server: {0}", e.Message));
			}

			// Create a new thread for packet handling
			if(tcpClient.Connected) {
				clientThread = new Thread(new ThreadStart(ClientThread));
				clientThread.Start();
			}
		}

		void ClientThread() {
			while(tcpClient.Connected) {
				NetworkStream clientStream = tcpClient.GetStream();

				using(Packet packet = new Packet()) {
					try {
						packet.ReadPacket(clientStream);
					} catch(HeaderMismatchException hme) {
						Engine.WriteLog(string.Format("Header does not match: {0}", hme.header));
						continue;
					} catch(ConnectionInterruptedException) {
						break;
					}

					if(Engine.LOG_PACKETS) {
						Engine.WriteLog(string.Format("Packet Received: {0}", packet.Type.ToString()));
					}

					switch(packet.Type) {
						case Packet.PacketTypes.CLIENT_VERSIONPACKET: {
							if(OnVersionPacketReceived != null) {
								OnVersionPacketReceived();
							}
						} break;

						case Packet.PacketTypes.CLIENT_SERVERFULL: {
							if(OnServerFullPacketReceived != null) {
								OnServerFullPacketReceived();
							}
						} break;

						case Packet.PacketTypes.CLIENT_LOBBY_CHATMESSAGE: {
							if(OnChatPacketReceived != null) {
								string username = packet.ReadString();
								string message = packet.ReadString();

								OnChatPacketReceived(username, message);
							}
						} break;

						case Packet.PacketTypes.CLIENT_LOBBY_START_GAME: {
							if(OnStartGamePacketReceived != null) {
								OnStartGamePacketReceived();
							}
						} break;

						case Packet.PacketTypes.CLIENT_GAME_INIT_DATA: {
							if(OnInitialDataPacketReceived != null) {
								ServerPlayer[] playerList = new ServerPlayer[10];

								for(int i = 0; i < playerList.Length; ++i) {
									playerList[i] = new ServerPlayer();
									playerList[i].X = packet.ReadInt();
									playerList[i].Y = packet.ReadInt();
									playerList[i].State = (PlayerStates)packet.ReadInt();
									playerList[i].spritePath = packet.ReadString();
								}

								OnInitialDataPacketReceived(playerList);
							}
						} break;

						case Packet.PacketTypes.CLIENT_GAME_UPDATE_DATA: {
							if(OnUpdateDataPacketReceived != null) {
								ServerPlayer[] playerList = new ServerPlayer[10];

								for(int i = 0; i < playerList.Length; ++i) {
									playerList[i] = new ServerPlayer();
									playerList[i].X = packet.ReadInt();
									playerList[i].Y = packet.ReadInt();
									playerList[i].State = (PlayerStates)packet.ReadInt();
								}

								OnUpdateDataPacketReceived(playerList);
							}
						} break;

						case Packet.PacketTypes.CLIENT_GAME_UPDATE_CROSSHAIR: {
							if(OnUpdateCrosshairPacketReceived != null) {
								int count = packet.ReadInt();

								Crosshair[] crosshairList = new Crosshair[count];
								for(int i = 0; i < count; ++i) {
									crosshairList[i] = new Crosshair();
									crosshairList[i].X = packet.ReadInt();
									crosshairList[i].Y = packet.ReadInt();
									crosshairList[i].r = packet.ReadInt();
									crosshairList[i].g = packet.ReadInt();
									crosshairList[i].b = packet.ReadInt();
									crosshairList[i].bulletCount = packet.ReadInt();
								}

								OnUpdateCrosshairPacketReceived(crosshairList);
							}
						} break;

						case Packet.PacketTypes.CLIENT_GAME_LOADED: {
							if(OnEveryoneLoadedPacketReceived != null) {
								OnEveryoneLoadedPacketReceived();
							}
						} break;

						case Packet.PacketTypes.CLIENT_GAME_UPDATE_BULLETCOUNT: {
							if(OnUpdateBulletCountPacketReceived != null) {
								int bulletCount = packet.ReadInt();
								OnUpdateBulletCountPacketReceived(bulletCount);
							}
						} break;

						case Packet.PacketTypes.CLIENT_GAME_WINNER: {
							if(OnWinnerPacketReceived != null) {
								string username = packet.ReadString();
								OnWinnerPacketReceived(username);
							}
						} break;
					}
				}
			}

			CloseConnection();
		}

		public void CloseConnection() {
			if(tcpClient != null) {
				if(tcpClient.Connected) {
					tcpClient.Close();
				}
			}
		}
	}
}
